package cn.cleanarch.infra.idp.pms.controller;

import cn.cleanarch.infra.idp.pms.constants.ApiPathConstants;
import cn.cleanarch.infra.idp.pms.domain.dto.DesignFileVersionDTO;
import cn.cleanarch.infra.idp.pms.domain.entity.DesignFile;
import cn.cleanarch.infra.idp.pms.service.DesignFileService;
import cn.cleanarch.infra.idp.pms.utils.SecurePreviewTokenUtil;
import cn.cleanarch.infra.idp.web.model.Result;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.SneakyThrows;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

/**
 * 安全设计文件预览控制器
 * 使用令牌机制确保只有授权用户可以访问HTML压缩包解压后的内容
 */
@RestController
@RequestMapping("/secure-preview")
public class SecurePreviewController {

    private static final Logger logger = LoggerFactory.getLogger(SecurePreviewController.class);

    @Autowired
    private DesignFileService designFileService;
    
    @Autowired
    private SecurePreviewTokenUtil securePreviewTokenUtil;

    /**
     * 获取分享预览URL（支持多种过期时间）
     * 
     * @param fileId 文件ID
     * @param expireType 过期类型: permanent(永久), 1d(1天), 3d(3天), 7d(7天), 1m(1个月), 3m(3个月), 12m(12个月)
     * @return 分享预览URL
     */
    @PostMapping("/file/{fileId}/share-preview-url/{expireType}")
    public Result getSharePreviewUrl(@PathVariable("fileId") Long fileId, @PathVariable("expireType") String expireType) {
        try {
            // 验证文件是否存在
            DesignFile designFile = designFileService.getById(fileId);
            if (designFile == null) {
                return Result.fail("文件不存在");
            }
            
            // 根据过期类型生成过期时间
            Long expireTime = getExpireTimeByType(expireType);
            
            // 生成签名参数
            SecurePreviewTokenUtil.SignParams signParams = securePreviewTokenUtil.generateSignParams(fileId, expireTime);
            
            /**
             * 对应{@link cn.cleanarch.infra.idp.pms.config.BaseWebConfiguration}的addResourceHandlers方法
             */
            String sharePreviewUrl = ApiPathConstants.SECURE_PREVIEW_FILE_SHARE_URL
                    .replace("{fileId}", fileId+"")
                    .replace("{timeStamp}", System.currentTimeMillis()+"")
                    + "&nonce=" + signParams.nonce 
                    + "&timestamp=" + signParams.timestamp 
                    + "&sign=" + signParams.sign;
            
            logger.info("生成分享预览URL - 文件ID: {}, 过期类型: {}, 过期时间: {}, 预览URL: {}", 
                       fileId, expireType, expireTime, sharePreviewUrl);
            return Result.data(sharePreviewUrl);
        } catch (Exception e) {
            logger.error("生成分享预览URL失败 - 文件ID: {}, 过期类型: {}", fileId, expireType, e);
            return Result.fail("生成分享预览URL失败: " + e.getMessage());
        }
    }

    /**
     * 预览HTML压缩包内容（通过Controller统一处理鉴权）
     *
     * @param fileId 文件ID
     * @param request HTTP请求
     * @param response HTTP响应
     * @throws Exception 异常
     */
    @GetMapping("/file/{fileId}/preview/index.html")
    public void previewFileHtmlZipContent(@PathVariable("fileId") Long fileId,
                                          HttpServletRequest request,
                                          HttpServletResponse response) throws Exception {
        // 验证令牌
        SecurePreviewTokenUtil.TokenValidationResult tokenResult = validateTokenAndFile(fileId, request, response);
        if (tokenResult == null) {
            // 验证失败，响应已在validateTokenAndFile方法中设置
            return;
        }

        // 验证文件是否存在
        DesignFile designFile = designFileService.getById(fileId);
        if (designFile == null) {
            logger.error("文件不存在 - 文件ID: {}", fileId);
            response.setStatus(HttpServletResponse.SC_NOT_FOUND);
            response.setContentType("text/plain; charset=UTF-8");
            response.getWriter().write("文件不存在");
            return;
        }

        previewFileHtmlZipSubPath(fileId,request,response);
    }

    /**
     * 预览HTML压缩包子路径资源（通过Controller统一处理鉴权）
     *
     * @param fileId 文件ID
     * @param request HTTP请求
     * @param response HTTP响应
     * @throws Exception 异常
     */
    @GetMapping("/file/{id}/preview/**")
    public void previewFileHtmlZipSubPath(@PathVariable("id") Long fileId,
                                          HttpServletRequest request,
                                          HttpServletResponse response) throws Exception {
        // 提取子路径
        String path = request.getRequestURI();
        String basePath = ApiPathConstants.SECURE_PREVIEW_FILE.replace("{fileId}",fileId+"");
        String subPath = path.startsWith(basePath) ? path.substring(basePath.length()) : "";

        logger.info("预览子路径资源 - 文件ID: {}, 子路径: {}, nonce: {}, timestamp: {}",
                fileId, subPath, request.getParameter("nonce"), request.getParameter("timestamp"));

        // 验证文件是否存在
        DesignFile designFile = designFileService.getById(fileId);
        if (designFile == null) {
            logger.error("文件不存在 - 文件ID: {}", fileId);
            response.setStatus(HttpServletResponse.SC_NOT_FOUND);
            response.setContentType("text/plain; charset=UTF-8");
            response.getWriter().write("文件不存在");
            return;
        }

        // 调用服务处理HTML压缩包子路径资源预览
        designFileService.serveFileSubPath(fileId, subPath, response);
//        designFileService.serveVersionSubPath(designFile.getCurrentVersionId(), subPath, response);
    }

    /**
     * 验证令牌和文件
     *
     * @param id 文件ID
     * @param request HTTP请求
     * @param response HTTP响应
     * @return 令牌验证结果，如果验证失败返回null
     */
    @SneakyThrows
    private SecurePreviewTokenUtil.TokenValidationResult validateTokenAndFile(Long id, HttpServletRequest request, HttpServletResponse response) {
        // 从请求参数中提取签名相关参数
        String nonce = request.getParameter("nonce");
        String timestampStr = request.getParameter("timestamp");
        String sign = request.getParameter("sign");

        if (nonce == null || timestampStr == null || sign == null) {
            logger.warn("预览请求中缺少签名参数 - 文件ID: {}, nonce: {}, timestamp: {}, sign: {}",
                    id, nonce, timestampStr, sign);
            response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
            response.setContentType("text/plain; charset=UTF-8");
            response.getWriter().write("请求格式错误：缺少签名参数");
            return null;
        }

        Long timestamp;
        try {
            timestamp = Long.parseLong(timestampStr);
        } catch (NumberFormatException e) {
            logger.warn("时间戳格式错误 - 文件ID: {}, timestamp: {}", id, timestampStr);
            response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
            response.setContentType("text/plain; charset=UTF-8");
            response.getWriter().write("请求格式错误：时间戳格式错误");
            return null;
        }

        // 对接收到的签名进行URL解码，处理+号被转换为空格的问题
        String decodedSign;
        try {
            decodedSign = java.net.URLDecoder.decode(sign, java.nio.charset.StandardCharsets.UTF_8);
        } catch (Exception e) {
            logger.warn("签名URL解码失败 - 文件ID: {}, sign: {}", id, sign);
            decodedSign = sign; // 如果解码失败，使用原始签名
        }

        // 验证签名参数
        SecurePreviewTokenUtil.TokenValidationResult result =
                securePreviewTokenUtil.validateSignParamsForPreview(id, timestamp, nonce, decodedSign);

        if (!result.isValid()) {
            logger.warn("预览签名验证失败 - 文件ID: {}, nonce: {}, 错误: {}", id, nonce, result.getMessage());
            response.setStatus(HttpServletResponse.SC_FORBIDDEN);
            response.setContentType("text/plain; charset=UTF-8");
            response.getWriter().write("访问被拒绝: " + result.getMessage());
            return null;
        }

        logger.info("预览签名验证成功 - 文件ID: {}, nonce: {}", id, nonce);
        return result;
    }

    /**
     * 获取版本分享预览URL（支持多种过期时间）
     * 
     * @param versionId 版本ID
     * @param expireType 过期类型: permanent(永久), 1d(1天), 3d(3天), 7d(7天), 1m(1个月), 3m(3个月), 12m(12个月)
     * @return 分享预览URL
     */
    @PostMapping("/version/{versionId}/share-preview-url/{expireType}")
    public Result getVersionSharePreviewUrl(@PathVariable("versionId") Long versionId, @PathVariable("expireType") String expireType) {
        try {
            // 验证版本是否存在
            DesignFileVersionDTO version = designFileService.getVersionById(versionId);
            if (version == null) {
                return Result.fail("版本不存在");
            }
            
            // 根据过期类型生成过期时间
            Long expireTime = getExpireTimeByType(expireType);
            
            // 生成签名参数
            SecurePreviewTokenUtil.SignParams signParams = securePreviewTokenUtil.generateSignParams(versionId, expireTime);
            
            /**
             * 对应{@link cn.cleanarch.infra.idp.pms.config.BaseWebConfiguration}的addResourceHandlers方法
             */
            String sharePreviewUrl = ApiPathConstants.SECURE_PREVIEW_VERSION_SHARE_URL
                    .replace("{versionId}", versionId+"")
                    .replace("{timeStamp}", System.currentTimeMillis()+"")
                    + "&nonce=" + signParams.nonce 
                    + "&timestamp=" + signParams.timestamp 
                    + "&sign=" + signParams.sign;
            
            logger.info("生成分享预览URL - 版本ID: {}, 过期类型: {}, 过期时间: {}, 预览URL: {}", 
                       versionId, expireType, expireTime, sharePreviewUrl);
            return Result.data(sharePreviewUrl);
        } catch (Exception e) {
            logger.error("生成分享预览URL失败 - 版本ID: {}, 过期类型: {}", versionId, expireType, e);
            return Result.fail("生成分享预览URL失败: " + e.getMessage());
        }
    }

    /**
     * 预览版本HTML压缩包内容（通过Controller统一处理鉴权）
     *
     * @param versionId 版本ID
     * @param request HTTP请求
     * @param response HTTP响应
     * @throws Exception 异常
     */
    @GetMapping("/version/{versionId}/preview/index.html")
    public void previewVersionHtmlZipContent(@PathVariable("versionId") Long versionId,
                                           HttpServletRequest request,
                                           HttpServletResponse response) throws Exception {
        // 验证令牌
        SecurePreviewTokenUtil.TokenValidationResult tokenResult = validateTokenAndVersion(versionId, request, response);
        if (tokenResult == null) {
            // 验证失败，响应已在validateTokenAndVersion方法中设置
            return;
        }

        // 验证版本是否存在
        DesignFileVersionDTO version = designFileService.getVersionById(versionId);
        if (version == null) {
            logger.error("版本不存在 - 版本ID: {}", versionId);
            response.setStatus(HttpServletResponse.SC_NOT_FOUND);
            response.setContentType("text/plain; charset=UTF-8");
            response.getWriter().write("版本不存在");
            return;
        }

        previewVersionHtmlZipSubPath(versionId, request, response);
    }

    /**
     * 预览版本HTML压缩包子路径资源（通过Controller统一处理鉴权）
     *
     * @param versionId 版本ID
     * @param request HTTP请求
     * @param response HTTP响应
     * @throws Exception 异常
     */
    @GetMapping("/version/{versionId}/preview/**")
    public void previewVersionHtmlZipSubPath(@PathVariable("versionId") Long versionId,
                                           HttpServletRequest request,
                                           HttpServletResponse response) throws Exception {
        // 提取子路径
        String path = request.getRequestURI();
        String basePath = ApiPathConstants.SECURE_PREVIEW_VERSION.replace("{versionId}", versionId+"");
        String subPath = path.startsWith(basePath) ? path.substring(basePath.length()) : "";

        logger.info("预览版本子路径资源 - 版本ID: {}, 子路径: {}, nonce: {}, timestamp: {}", 
                   versionId, subPath, request.getParameter("nonce"), request.getParameter("timestamp"));

        // 验证版本是否存在
        DesignFileVersionDTO version = designFileService.getVersionById(versionId);
        if (version == null) {
            logger.error("版本不存在 - 版本ID: {}", versionId);
            response.setStatus(HttpServletResponse.SC_NOT_FOUND);
            response.setContentType("text/plain; charset=UTF-8");
            response.getWriter().write("版本不存在");
            return;
        }

        // 调用服务处理版本HTML压缩包子路径资源预览
        designFileService.serveVersionSubPath(versionId, subPath, response);
    }

    /**
     * 验证令牌和版本
     *
     * @param versionId 版本ID
     * @param request HTTP请求
     * @param response HTTP响应
     * @return 令牌验证结果，如果验证失败返回null
     */
    @SneakyThrows
    private SecurePreviewTokenUtil.TokenValidationResult validateTokenAndVersion(Long versionId, HttpServletRequest request, HttpServletResponse response) {
        // 从请求参数中提取签名相关参数
        String nonce = request.getParameter("nonce");
        String timestampStr = request.getParameter("timestamp");
        String sign = request.getParameter("sign");

        if (nonce == null || timestampStr == null || sign == null) {
            logger.warn("预览请求中缺少签名参数 - 版本ID: {}, nonce: {}, timestamp: {}, sign: {}", 
                       versionId, nonce, timestampStr, sign);
            response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
            response.setContentType("text/plain; charset=UTF-8");
            response.getWriter().write("请求格式错误：缺少签名参数");
            return null;
        }

        Long timestamp;
        try {
            timestamp = Long.parseLong(timestampStr);
        } catch (NumberFormatException e) {
            logger.warn("时间戳格式错误 - 版本ID: {}, timestamp: {}", versionId, timestampStr);
            response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
            response.setContentType("text/plain; charset=UTF-8");
            response.getWriter().write("请求格式错误：时间戳格式错误");
            return null;
        }

        // 对接收到的签名进行URL解码，处理+号被转换为空格的问题
        String decodedSign;
        try {
            decodedSign = java.net.URLDecoder.decode(sign, java.nio.charset.StandardCharsets.UTF_8);
        } catch (Exception e) {
            logger.warn("签名URL解码失败 - 版本ID: {}, sign: {}", versionId, sign);
            decodedSign = sign; // 如果解码失败，使用原始签名
        }

        // 验证签名参数
        SecurePreviewTokenUtil.TokenValidationResult result = 
            securePreviewTokenUtil.validateSignParamsForPreview(versionId, timestamp, nonce, decodedSign);

        if (!result.isValid()) {
            logger.warn("预览签名验证失败 - 版本ID: {}, nonce: {}, 错误: {}", versionId, nonce, result.getMessage());
            response.setStatus(HttpServletResponse.SC_FORBIDDEN);
            response.setContentType("text/plain; charset=UTF-8");
            response.getWriter().write("访问被拒绝: " + result.getMessage());
            return null;
        }

        logger.info("预览签名验证成功 - 版本ID: {}, nonce: {}", versionId, nonce);
        return result;
    }

    /**
     * 根据过期类型获取过期时间（毫秒）
     *
     * @param expireType 过期类型
     * @return 过期时间（毫秒）
     */
    private Long getExpireTimeByType(String expireType) {
        switch (expireType.toLowerCase()) {
            case "permanent":
                return null; // 永久，不设置过期时间
            case "1d":
                return 24L * 60 * 60 * 1000; // 1天
            case "3d":
                return 3L * 24 * 60 * 60 * 1000; // 3天
            case "7d":
                return 7L * 24 * 60 * 60 * 1000; // 7天
            case "1m":
                return 30L * 24 * 60 * 60 * 1000; // 1个月（按30天计算）
            case "3m":
                return 3L * 30 * 24 * 60 * 60 * 1000; // 3个月
            case "12m":
                return 12L * 30 * 24 * 60 * 60 * 1000; // 12个月
            default:
                // 默认永久
                return null;
        }
    }
}